"""Authentication service - 极简版本 (兼容 AsyncSession/Session)."""

from __future__ import annotations

from datetime import UTC, datetime, timedelta

from fastapi import HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Session

from app.core.config import settings
from app.core.security import create_access_token, decode_token, get_password_hash, verify_password
from app.models.user import DEFAULT_ROLE, USER_ROLES, User
from app.schemas.token import TokenPair
from app.schemas.user import UserCreate, UserUpdate


class AuthService:
    """核心认证服务: 登录/注册/Token刷新/密码重置."""

    def __init__(self, session: AsyncSession | Session) -> None:
        self.session = session

    # ----- Internal helpers to support both async/sync sessions -----
    async def _execute(self, stmt):
        if isinstance(self.session, AsyncSession):
            return await self.session.execute(stmt)
        return self.session.execute(stmt)

    async def _flush(self) -> None:
        if isinstance(self.session, AsyncSession):
            await self.session.flush()
        else:
            self.session.flush()

    async def _refresh(self, instance) -> None:
        if isinstance(self.session, AsyncSession):
            await self.session.refresh(instance)
        else:
            self.session.refresh(instance)

    async def authenticate(self, identifier: str, password: str) -> TokenPair:
        """
        用户登录认证.

        Args:
            identifier: 邮箱或用户名
            password: 密码
        """
        user = await self._get_user_by_email(identifier)
        if user is None:
            user = await self._get_user_by_full_name(identifier)

        if user is None or not verify_password(password, user.hashed_password):
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials")

        if not user.is_active:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User is inactive")

        return self._create_tokens(user)

    async def register(self, data: UserCreate) -> User:
        """用户注册."""
        existing = await self._get_user_by_email(data.email)
        if existing:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered")

        role = data.role if data.role in USER_ROLES else DEFAULT_ROLE
        user = User(
            email=data.email,
            hashed_password=get_password_hash(data.password),
            full_name=data.full_name,
            role=role,
            is_admin=role == "admin",
        )
        self.session.add(user)
        await self._flush()
        await self._refresh(user)
        return user

    async def refresh(self, refresh_token: str) -> TokenPair:
        """刷新访问令牌."""
        payload = decode_token(refresh_token, expected_type="refresh")
        user_id = payload.get("sub")
        if user_id is None:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid refresh token")

        result = await self._execute(select(User).where(User.id == int(user_id)))
        user = result.scalar_one_or_none()
        if user is None or not user.is_active:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not available")

        return self._create_tokens(user)

    async def reset_password_direct(self, email: str, new_password: str, confirm_password: str) -> None:
        """直接重置密码(无需邮件验证)."""
        if new_password != confirm_password:
            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Passwords do not match")

        user = await self._get_user_by_email(email)
        if not user:
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")

        if not user.is_active:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="User is inactive")

        user.hashed_password = get_password_hash(new_password)
        await self._flush()

    async def update_current_user(self, user: User, data: UserUpdate) -> User:
        """Update current user's profile."""
        if data.email is not None and data.email != user.email:
            existing = await self._get_user_by_email(data.email)
            if existing:
                raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered")
            user.email = data.email

        if data.full_name is not None:
            user.full_name = data.full_name

        if data.password is not None:
            user.hashed_password = get_password_hash(data.password)

        await self._flush()
        await self._refresh(user)
        return user

    async def _get_user_by_email(self, email: str) -> User | None:
        result = await self._execute(select(User).where(User.email == email))
        return result.scalar_one_or_none()

    async def _get_user_by_full_name(self, full_name: str) -> User | None:
        result = await self._execute(select(User).where(User.full_name == full_name))
        return result.scalar_one_or_none()

    def _create_tokens(self, user: User) -> TokenPair:
        """生成访问令牌和刷新令�?"""
        access_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
        refresh_expires = timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
        issued_at = datetime.now(UTC)

        access_token = create_access_token(str(user.id), "access", access_expires)
        refresh_token = create_access_token(str(user.id), "refresh", refresh_expires)

        return TokenPair(
            access_token=access_token,
            refresh_token=refresh_token,
            expires_at=issued_at + access_expires,
        )
